﻿using System;
using System.Data;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Kenly.DBFramework;
using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Reflection
{
    /// <summary>
    /// Reflect the properties to parameters prepared for inserting or updating a row.
    /// </summary>
    internal static class ParameterReflection
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbType"></param>
        /// <param name="entity"></param>
        /// <param name="updatedById"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        public static List<IDataParameter> UpdateByIdPK(DatabaseType dbType, object entity, bool updatedById, List<string> propertyNames)
        {
            return PropertiesToParametersForUpdate(dbType, entity, updatedById, propertyNames);
        }

        /// <summary>
        /// The property which contains a ColumnAttribute is reflect to parameter's name,value and direction.Not including identity.
        /// </summary>
        /// <param name="dbType"></param>
        /// <param name="entity"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        public static List<IDataParameter> Insert(DatabaseType dbType, object entity, List<string> propertyNames)
        {
            return PropertiesToParametersForInsert(dbType, entity,propertyNames);
        }

        public static IDataParameter GetIdentityParameter(DatabaseType dbType, object entity, bool ouputIdentity)
        {
            string idName = IdentityUtility.GetIdentityName(entity.GetType());
            if (string.IsNullOrEmpty(idName))
            {
                throw new GatewayException(MsgResource.IdentityOfEntityMissing, entity);
            }
            PropertyInfo idProperty= IdentityUtility.GetIdentityProperty(entity.GetType());

            IParameterBuilder parameterBuilder = ParameterBuilderFactory.CreateInstance(dbType);
            IDataParameter para = parameterBuilder.CreateColumnParameter(entity,idProperty);
            para.Direction = ouputIdentity ? ParameterDirection.Output : ParameterDirection.Input;

            return para;

        }


        /// <summary>
        /// The property which contains a ColumnAttribute is reflect to parameter's name and value.
        /// </summary>
        /// <param name="dbType"></param>
        /// <param name="entity"></param>
        /// <param name="updatedById"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        private static List<IDataParameter> PropertiesToParametersForUpdate(DatabaseType dbType, object entity, bool updatedById, List<string> propertyNames)
        {
            if (entity == null)
            {
                throw new GatewayException(MsgResource.EmptyEntity);
            }
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                throw new GatewayException(MsgResource.InvalidArguments, "List<string> propertyNames");
            }

            List<IDataParameter> parameters = new List<IDataParameter>();
            string columnName = "";
            object columnValue = null;
            IDataParameter para = null;

            IParameterBuilder parameterBuilder = ParameterBuilderFactory.CreateInstance(dbType);

            ColumnAttribute columnAttribute = null;
            foreach (string propertyName in propertyNames)
            {
                foreach (System.Reflection.PropertyInfo propertyInfo in entity.GetType().GetProperties())
                {
                    if (propertyInfo.Name != propertyName)
                    {
                        continue;
                    }
                    columnAttribute = AttributeUtility.GetColumnAttribute(propertyInfo);
                    if (columnAttribute == null)
                    {
                        continue;
                    }
                    if (columnAttribute.IsIdentity)
                    {
                        continue;
                    }
                    if (!updatedById && columnAttribute.IsPrimaryKey)
                    {
                        continue;
                    }

                    columnValue = propertyInfo.GetValue(entity, null);
                    if (columnValue == null)
                    {
                        columnValue = DBNull.Value;
                    }
                    columnName = string.IsNullOrEmpty(columnAttribute.ColumnName) ? propertyInfo.Name : columnAttribute.ColumnName;
                    para = parameterBuilder.CreateColumnParameter(columnAttribute,columnName, columnValue);
                    parameters.Add(para);
                }
            }

            return parameters;
        }


        /// <summary>
        /// The property which contains a ColumnAttribute is reflect to parameter's name and value.
        /// </summary>
        /// <param name="dbType"></param>
        /// <param name="entity"></param>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        private static List<IDataParameter> PropertiesToParametersForInsert(DatabaseType dbType, object entity, List<string> propertyNames)
        {
            if (entity == null)
            {
                throw new GatewayException(MsgResource.EmptyEntity);
            }
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                throw new GatewayException(MsgResource.InvalidArguments, "List<string> propertyNames");
            }

            List<IDataParameter> parameters = new List<IDataParameter>();
            string columnName = "";
            object columnValue = null;
            IDataParameter para = null;

            IParameterBuilder parameterBuilder = ParameterBuilderFactory.CreateInstance(dbType);

            ColumnAttribute columnAttribute = null;
            foreach (string propertyName in propertyNames)
            {
                foreach (System.Reflection.PropertyInfo propertyInfo in entity.GetType().GetProperties())
                {
                    if (propertyInfo.Name != propertyName)
                    {
                        continue;
                    }
                    columnAttribute = AttributeUtility.GetColumnAttribute(propertyInfo);
                    if (columnAttribute == null)
                    {
                        continue;
                    }
                    if (columnAttribute.IsIdentity)
                    {
                        continue;
                    }
                    columnValue = propertyInfo.GetValue(entity, null);
                    if (columnValue == null)
                    {
                        columnValue = DBNull.Value;
                    }
                    columnName = string.IsNullOrEmpty(columnAttribute.ColumnName) ? propertyInfo.Name : columnAttribute.ColumnName;
                    para = parameterBuilder.CreateColumnParameter(columnAttribute, columnName, columnValue);
                    parameters.Add(para);
                }
            }

            return parameters;
        }

    }
}


